package a5.environment;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import a5.function.Constants;
import a5.function.Function;

/**
 * <pre>
 * Basic implementation of an environment. This environment emplores a global scoping policy. 
 * Once defined, a variable/function is available at all points of program execution.
 * Additionally, variables and functions may be overwritten without error. This 
 * applies to user defined functions as well as functions injected at instantiation.
 * Additionally, there are no limitations on using the same name for both a variable and a function.
 * That is, 'x' could refer to both a variable and a function.  
 * 
 * All setter methods as well as the {@code Maps} which back the variable and function collections
 * are synchonized. Thus, this environment itself is thread safe. 
 * 
 * Note: The {@code a5.environment.function.Function Function} returned by {@link #getFunction(String)}
 * is NOT guaranteed to be thread safe. Thread safety of this class is determined by the 
 * {@code Function} implementation.
 * </pre>
 * 
 * @author Jason Campos
 * @version Assignment 6, CS 152 (Programming Paradigms) Fall 2012, Jeffrey Smith
 */
public class EnvironmentImpl implements Environment {
	// Storage for globally available variables 
	private final Map<String,String> variables;
	// Storage for globally available functions
	private final Map<String,Function> functions;
	
	/**
	 * Instantiates the environment loads any default variables and functions.
	 */
	public EnvironmentImpl(){
		variables = Collections.synchronizedMap(new HashMap<String,String>());
		functions = Collections.synchronizedMap(new HashMap<String,Function>());
		functions.putAll(Constants.functions);
	}
	
	/*
	 * (non-Javadoc)
	 * @see a5.environment.Environment#setVariable(java.lang.String, java.lang.String)
	 */
	@Override
	public synchronized String setVariable(String variableName, String value){
		return variables.put(variableName, value);
	}
	
	/*
	 * (non-Javadoc)
	 * @see a5.environment.Environment#getVariable(java.lang.String)
	 */
	@Override
	public String getVariable(String variableName) throws IllegalArgumentException{
		if(!variables.containsKey(variableName))
			throw new IllegalArgumentException("variable not found: " + variableName);
		return variables.get(variableName);
	}
	
	/*
	 * (non-Javadoc)
	 * @see a5.environment.Environment#setFunction(java.lang.String, a5.function.Function)
	 */
	@Override
	public synchronized Function setFunction(String functionName, Function function){
		return functions.put(functionName, function);
	}
	
	/*
	 * (non-Javadoc)
	 * @see a5.environment.Environment#getFunction(java.lang.String)
	 */
	@Override
	public Function getFunction(String functionName) throws IllegalArgumentException{
		if(!functions.containsKey(functionName))
			throw new IllegalArgumentException("function not found: " + functionName);
		return functions.get(functionName);
	}

	/*
	 * (non-Javadoc)
	 * @see a5.environment.Environment#getConstantFunctions()
	 */
	@Override
	public Map<String,Function> getConstantFunctions() {
		return Collections.unmodifiableMap(Constants.functions);
	}

	/*
	 * (non-Javadoc)
	 * @see a5.environment.Environment#getConstantVariables()
	 */
	@Override
	public Map<String, String> getConstantVariables() {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * @see a5.environment.Environment#pushFrame()
	 */
	@Override
	public Environment pushFrame(String envName){
		// No implementation. Variables and functions are global.
		return this;
	}
	
	/** 
	 * (non-Javadoc)
	 * @see a5.environment.Environment#popFrame()
	 */
	@Override
	public Environment popFrame(){
		// No implementation. Variables and functions are global.	
		return this;
	}
}
